package com.manager.modelserver.service.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import com.manager.modelserver.Utils.ArraysUtils;
import com.manager.modelserver.entry.BasBomList;
import com.manager.modelserver.entry.BomListData;
import com.manager.modelserver.mapper.BasBomListMapper;
import com.manager.modelserver.redis.JedisPoolUtils;
import com.manager.modelserver.service.IBasBomListService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionTemplate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import redis.clients.jedis.Jedis;


import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;


// 有个很重要的点 DemoDataListener 不能被spring管理，要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener extends AnalysisEventListener<BomListData> {
    public static final String BASBOMLIST_SAVE = "basbomlist_save";//redis Key
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
    private ExecutorService executorService = Executors.newFixedThreadPool(7);
    private AtomicInteger atomicInteger;
    /**
     * 每隔5条存储数据库，实际使用中可以3000条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 200000;
    /**
     * 缓存的数据
     */
    private List<BomListData> list = new ArrayList<>(BATCH_COUNT);
    /**
     * 假设这个是一个DAO，当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private IBasBomListService basBomListService;
    private SqlSessionTemplate sqlSessionTemplate;

    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param basBomListService
     */
    public DemoDataListener(IBasBomListService basBomListService, SqlSessionTemplate sqlSessionTemplate) {
        this.basBomListService = basBomListService;
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(BomListData data, AnalysisContext context) {
        list.add(data);
        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            list = new ArrayList<>(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        saveData();
        LOGGER.info("所有数据解析完成:"+atomicInteger.get());

    }

    /**
     * 加上存储数据库
     */
    private void saveData() { //200000 条数据保存。多线程保存
        basBomListService.remove(null);//删除原所有数据
        LOGGER.info("{}条数据，开始存储数据库！", list.size());
        List<BasBomList> saveList = new ArrayList<>();
        for (int i = 0; i <list.size() ; i++) {
            if (i == 0)continue;
            BasBomList addbean = new BasBomList();
            BeanUtils.copyProperties(list.get(i),addbean);
            addbean.setOfficeCode("SZWLC");
            addbean.setBomUuid(UUID.randomUUID().toString().replaceAll("-",""));
            saveList.add(addbean);
        }
        Jedis jedis = JedisPoolUtils.getPoolJedis();
        //推荐写法
        try{
            //执行
            System.out.println(JedisPoolUtils.getPoolJedis().ping());
            jedis.set(BASBOMLIST_SAVE,JSON.toJSONString(saveList));
            String s = jedis.get(BASBOMLIST_SAVE);
            List<BasBomList> basBomLists = JSON.parseArray(s, BasBomList.class);
            List<List<BasBomList>> lists = ArraysUtils.splitList(basBomLists, 1000);
            List<Future> futurelist = new ArrayList<>();
             atomicInteger = new AtomicInteger();
            for (List<BasBomList> list : lists){
                Future<List<BasBomList>> future = executorService.submit(new SaveTask(basBomListService,list,sqlSessionTemplate,atomicInteger));
                futurelist.add(future);
            }
            for (Future future:futurelist){
                List<BasBomList> errorList = (List<BasBomList>) future.get();
                if (errorList != null && errorList.size() > 0){
                    autoSaveBasBomList(basBomListService,errorList,sqlSessionTemplate,atomicInteger);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if ( JedisPoolUtils.jedis  != null){
                JedisPoolUtils.jedis.close();
            }
        }
        LOGGER.info("存储数据库成功！");
    }
    //保存错误的数据递归无限保存
    private void autoSaveBasBomList(IBasBomListService basBomListService,List<BasBomList> basBomLists,SqlSessionTemplate sqlSessionTemplate,AtomicInteger atomicInteger) throws ExecutionException, InterruptedException {
        if (basBomLists == null || basBomLists.size() == 0){
            return ;
        }
        Future<List<BasBomList>> future = executorService.submit(new SaveTask(basBomListService,basBomLists,sqlSessionTemplate,atomicInteger));
        List<BasBomList> errorList = future.get();
        autoSaveBasBomList(basBomListService,errorList,sqlSessionTemplate,atomicInteger);
    }
    class SaveTask implements Callable<List<BasBomList>> {
        private IBasBomListService basBomListService;
        private List<BasBomList> basBomLists;
        private AtomicInteger atomicInteger;
        private SqlSessionTemplate sqlSessionTemplate;
        public SaveTask(IBasBomListService basBomListService,List<BasBomList> basBomLists,SqlSessionTemplate sqlSessionTemplate,AtomicInteger atomicInteger) {
            this.basBomListService = basBomListService;
            this.basBomLists = basBomLists;
            this.atomicInteger = atomicInteger;
            this.sqlSessionTemplate = sqlSessionTemplate;
        }


        @Override
        public List<BasBomList> call() throws Exception {
            Logger logger = LoggerFactory.getLogger(SaveTask.class);
            long timeStart = System.currentTimeMillis();
           logger.info(Thread.currentThread().getName()+"开始保存");
               SqlSession session = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
               try {
                   BasBomListMapper mapper = session.getMapper(BasBomListMapper.class);
                   for (BasBomList bean : basBomLists){
                       mapper.insert(bean);
                   }
                   session.commit();
                   logger.info("耗时"+String.valueOf(System.currentTimeMillis()- timeStart) +" 数据"+basBomLists.size()+"总共"+atomicInteger.getAndAdd(basBomLists.size()));
                   return null;
           }catch (Exception e){
                   e.printStackTrace();
                    return basBomLists;
           }finally {
                   session.close();
               }

        }
    }

    public static void main(String[] args) {
        System.out.println(UUID.randomUUID().toString().replaceAll("-","").length());
        List<BasBomList> saveList = new ArrayList<>();
        BasBomList addbean = new BasBomList();
        addbean.setBomUuid("123123");
        addbean.setGoodsDesc("123123");
        addbean.setBomModel("123123");
        saveList.add(addbean);
        System.out.println(JSON.toJSONString(saveList));
    }
}